#include "ssd_cpftl.h"

int indexofseq = 0, indexofarr = 0;
int indexold = 0, indexnew = 0, index2nd=0;
int maxentry=0, MC=0;//分别为出现最多的项的值和出现的次数
int update_flag = 0;//shzb:用于判断连续缓存中的部分映射项更新标志是否置零
int sequential_count = 0;
int entry = 0;

/****************************************************
 *              CPFTL  主函数
 * **************************************************/
void CPFTL_Scheme(unsigned int *pageno,int *req_size,int operation)
{
    int blkno=(*pageno),cnt=(*req_size);
    int free_pos=-1,pos=-1;   //zan shi mei yong 
    Node *Temp;
    //初始化
    if (init_flag==0)
    {
        MAP_REAL_MAX_ENTRIES = 1792;
        real_arr=(int *)malloc(sizeof(int)*MAP_REAL_MAX_ENTRIES);
        MAP_SECOND_MAX_ENTRIES = 1024;
        second_arr=(int *)malloc(sizeof(int)*MAP_SECOND_MAX_ENTRIES);
        MAP_SEQ_MAX_ENTRIES = 1280;
        seq_arr=(int*)malloc(sizeof(int)*MAP_SEQ_MAX_ENTRIES);
        entry_arr=(int*)malloc(sizeof(int)*page_num_for_2nd_map_table);
        CPFTL_init_arr();
        init_flag=1;
    }
    
    /*******************正式进入仿真运行******************/
    // 此处开始CPFTL函数的逻辑
    if(opagemap[blkno].map_status==MAP_REAL){
        // 1. req in H-CMT
        CPFTL_Hit_HCMT(blkno,operation);
        blkno++;
        cache_cmt_hit++;
        rqst_cnt++;
    }
    else if(opagemap[blkno].map_status==MAP_SECOND || opagemap[blkno].map_status==MAP_SEQ){
        if(opagemap[blkno].map_status==MAP_SECOND){
            cache_scmt_hit++;
            CPFTL_move_CCMT_to_HCMT(blkno,operation);
        }else if(opagemap[blkno].map_status==MAP_SEQ){
            cache_slcmt_hit++;
            CPFTL_move_SCMT_to_HCMT(blkno,operation);
        }else{
            printf("should not come here!\n");
            assert(0);
        }
        blkno++;
        rqst_cnt++;
    }
    else if((cnt+1) >= 2 ){
        // 3. THRESHOLD=2,表示大于或等于4KB的请求，当作连续请求来处理
        //内部对blkno和cnt做了更新
        // debug
        int last_cnt=cnt;
        int last_blkno=blkno;
        
        CPFTL_pre_load_entry_into_SCMT(&blkno,&cnt,operation);

        //debug test
        if(cnt>0){
            int i=0;
            for(i=0;i<=last_cnt;i++){
                last_blkno++;
                if(opagemap[last_blkno].map_status==MAP_SECOND){
                    if(search_table(second_arr,MAP_SECOND_MAX_ENTRIES,last_blkno)==-1){
                        printf("error happend after CPFTL_pre_load_entry_into_SCMT\n");
                        printf("second_arr not include %d",last_blkno);
                        assert(0);
                    }
                }
            }
        }
    }
    else{
        //4. opagemap not in SRAM  must think about if map table in SRAM(C-CMT) is full
        CPFTL_CCMT_Is_Full();
        // load entry into C_CMT
        CPFTL_load_entry_into_CCMT(blkno,operation);

        //debug test
        if(opagemap[blkno].map_status==MAP_SECOND){
            if(search_table(second_arr,MAP_SECOND_MAX_ENTRIES,blkno)==-1){
                printf("load new-entry into second arr is failed\n");
                assert(0);
            }
        }
        blkno++;
        rqst_cnt++;
    }
    //CMT中的各种情况处理完毕
    // update return data
    (*pageno)=blkno;
    (*req_size)=cnt;
}


/****************************************************
 *              CPFTL  封装函数
 * **************************************************/


void CPFTL_init_arr()
{
    int i;
    for( i = 0; i < MAP_REAL_MAX_ENTRIES; i++) {
        real_arr[i] = -1;
    }
    for( i= 0; i < MAP_SEQ_MAX_ENTRIES; i++) {
        seq_arr[i] = -1;
    }
    for( i = 0; i < MAP_SECOND_MAX_ENTRIES; i++){
        second_arr[i] = -1;
    }
    for( i = 0; i < page_num_for_2nd_map_table; i++) {
        entry_arr[i] = 0;
    }
    for( i = 0; i < CACHE_MAX_ENTRIES; i++) {
        cache_arr[i] = -1;
    }
    MAP_REAL_NUM_ENTRIES=0;
    MAP_SECOND_NUM_ENTRIES=0;
    MAP_SEQ_NUM_ENTRIES=0;
}

void CPFTL_Hit_HCMT(int blkno,int operation)
{
	opagemap[blkno].map_status=MAP_REAL;
	opagemap[blkno].map_age=operation_time;
	operation_time++;
    // write or read data page 
    if(operation==0){
        write_count++;
        opagemap[blkno].update = 1;
    }
    else
        read_count++;

    send_flash_request(blkno*4, 4, operation, 1); 
}

void CPFTL_HCMT_Is_Full()
{
    int pos=-1,pos_2nd=-1;
    // 查看H_CMT是否满了
    if((MAP_REAL_MAX_ENTRIES - MAP_REAL_NUM_ENTRIES) == 0){  
        find_real_min();
        if(opagemap[real_min].update == 1){
            CPFTL_CCMT_Is_Full();
            //将H-CMT中更新的映射项剔除到C-CMT中
            entry = (real_min-page_num_for_2nd_map_table)/MAP_ENTRIES_PER_PAGE;
            entry_arr[entry] += 1;
            //chang_max_entry(entry, entry_arr[entry]);
            opagemap[real_min].map_status = MAP_SECOND;
            pos = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
            real_arr[pos]=-1;
            MAP_REAL_NUM_ENTRIES--;
            pos_2nd = find_free_pos(second_arr,MAP_SECOND_MAX_ENTRIES);
            second_arr[pos_2nd]=-1;
            second_arr[pos_2nd]=real_min;
            MAP_SECOND_NUM_ENTRIES++;
            //debug
            if(MAP_SECOND_NUM_ENTRIES > MAP_SECOND_MAX_ENTRIES){
                printf("The second cache is overflow!\n");
                assert(0);
            }

            //debug test
            if(opagemap[real_min].map_status==MAP_SECOND && search_table(second_arr,MAP_SECOND_MAX_ENTRIES,real_min)==-1){
                printf("not reset min_real:%d into second_arr\n",real_min);
                assert(0);
            }
        }else{
            // 没有更新直接剔除到flash
            pos = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
            real_arr[pos]=-1;
            opagemap[real_min].map_status = MAP_INVALID;
            opagemap[real_min].map_age = 0;
            MAP_REAL_NUM_ENTRIES--;
        }
    }
}


void CPFTL_pre_load_entry_into_SCMT(int *pageno,int *req_size,int operation)
{
	int blkno=(*pageno),cnt=(*req_size);
	int pos=-1,free_pos=-1;
	
	if(not_in_cache(blkno)==0){  //不在缓存中
		//~满足之后连续的pre_num个数的映射项不在CMT中才预取 
		if((MAP_SEQ_MAX_ENTRIES-MAP_SEQ_NUM_ENTRIES)==0){
			//~如果seq_arr满了
			for(indexofarr = 0;indexofarr < NUM_ENTRIES_PER_TIME;indexofarr++){
            //SEQ的替换策略是把SEQ的最前面几个映射项剔除出去，查看是否存在更新页，存在一个更新页，整个翻译页都得重新更细，update_flag置位
                if((opagemap[seq_arr[indexofarr]].update == 1)&&(opagemap[seq_arr[indexofarr]].map_status == MAP_SEQ))
                {
                    //update_reqd++;
                    update_flag=1;
                    opagemap[seq_arr[indexofarr]].update=0;
                    opagemap[seq_arr[indexofarr]].map_status = MAP_INVALID;
                    opagemap[seq_arr[indexofarr]].map_age = 0;
                }
                else if((opagemap[seq_arr[indexofarr]].update == 0)&&(opagemap[seq_arr[indexofarr]].map_status ==MAP_SEQ))
                {
                    opagemap[seq_arr[indexofarr]].map_status = MAP_INVALID;
                    opagemap[seq_arr[indexofarr]].map_age = 0;
                }
            }
            if(update_flag == 1)
            {
                //~这里的NUM_ENTRIES_PER_TIME必须远小于一个页
                send_flash_request(((seq_arr[0]-page_num_for_2nd_map_table)/MAP_ENTRIES_PER_PAGE)*4, 4, 1, 2);
                send_flash_request(((seq_arr[0]-page_num_for_2nd_map_table)/MAP_ENTRIES_PER_PAGE)*4, 4, 0, 2);
                update_flag=0;
            }
            //~ 根据FIFO移动数组数据覆盖 
            for(indexofarr = 0;indexofarr <= MAP_SEQ_MAX_ENTRIES-1-NUM_ENTRIES_PER_TIME; indexofarr++)
                seq_arr[indexofarr] = seq_arr[indexofarr+NUM_ENTRIES_PER_TIME];
            MAP_SEQ_NUM_ENTRIES-=NUM_ENTRIES_PER_TIME;
		}
        //~ 剔除依据FIFO完毕 ，开始预取
        flash_hit++;
        send_flash_request(((blkno-page_num_for_2nd_map_table)/MAP_ENTRIES_PER_PAGE)*4, 4, 1, 2);
        for(indexofseq=0; indexofseq < NUM_ENTRIES_PER_TIME;indexofseq++)//NUM_ENTRIES_PER_TIME在这里表示一次加载4个映射表信息
        {
            opagemap[blkno+indexofseq].map_status=MAP_SEQ;
            opagemap[blkno+indexofseq].map_age=operation_time;
            operation_time++;
            //~对应的seq命中转移数据页应该不能删除原来的数据 
            seq_arr[MAP_SEQ_NUM_ENTRIES] = (blkno+indexofseq);//加载到SEQ的映射项是放在SEQ尾部
            MAP_SEQ_NUM_ENTRIES++;
        }
        if(MAP_SEQ_NUM_ENTRIES > MAP_SEQ_MAX_ENTRIES)
        {
            printf("The sequential cache is overflow!\n");
            exit(0);
        }
        //~  对数据页读取写入操作更新 
        if(operation==0)
        {
            write_count++;
            opagemap[blkno].update=1;
        }
        else
            read_count++;
        send_flash_request(blkno*4, 4, operation, 1);

        blkno++;
        sequential_count = 0;
        //之后连续的请求因为映射加载完成直接读取写入操作
        for(;(cnt>0)&&(sequential_count<NUM_ENTRIES_PER_TIME-1);cnt--)
        {
            opagemap[blkno].map_age++;
            if(operation==0)
            {
                write_count++;
                opagemap[blkno].update=1;
            }
            else
                read_count++;
            send_flash_request(blkno*4, 4, operation,1);
            blkno++;
            rqst_cnt++;
            cache_slcmt_hit++;
            sequential_count++;
        }
        rqst_cnt++;
        //zhoujie
        *req_size=cnt;
        *pageno=blkno;
        
	}else{
		//~反之只是将其一个映射项加载到CCMT中 (根据前面的删选，只能是不存在CMT的映射项)
		CPFTL_CCMT_Is_Full();
		CPFTL_load_entry_into_CCMT(blkno,operation);
		blkno++;
		//zhoujie
		*req_size=cnt;
		*pageno=blkno;	
	}
	//连续加载处理完成
}


void CPFTL_load_entry_into_CCMT(int blkno,int operation)
{
    int free_pos=-1;
    flash_hit++;
    // read MVPN page 
    entry = (blkno-page_num_for_2nd_map_table)/MAP_ENTRIES_PER_PAGE;
    entry_arr[entry] += 1;
    //chang_max_entry(entry, entry_arr[entry]);
	send_flash_request(entry*4, 4, 1, 2);   // read from 2nd mapping table
	opagemap[blkno].map_status = MAP_SECOND;
    opagemap[blkno].map_age=operation_time;
    operation_time++;
    free_pos=find_free_pos(second_arr,MAP_SECOND_MAX_ENTRIES);
    if(free_pos==-1){
        printf("can not find free pos in second_arr\n");
        assert(0);
    }
	second_arr[free_pos]=blkno;
    MAP_SECOND_NUM_ENTRIES++;

    // write or read data page 
    if(operation==0){
        write_count++;
        opagemap[blkno].update = 1;
    }
    else
        read_count++;

    send_flash_request(blkno*4, 4, operation, 1); 
	
	// debug test 
	if(opagemap[blkno].map_status!=MAP_SECOND){
		printf("not set MLC_opagemap flag\n");
		assert(0);
	}
	if(search_table(second_arr,MAP_SECOND_MAX_ENTRIES,blkno)==-1){
		printf("not play lpn-entry:%d into CMT\n",blkno);
		assert(0);
	}
		
}


// C-CMT之后要修改
void CPFTL_CCMT_Is_Full()
{
    // int min_second=-1;
    // min_second=MLC_find_second_min();
	int i=0;
    //	int offset=0;
    // 若果满了,先选择关联度最大进行删除
    if(MAP_SECOND_MAX_ENTRIES-MAP_SECOND_NUM_ENTRIES==0){
        MC=0;
        find_MC_entries(entry_arr,page_num_for_2nd_map_table);
        send_flash_request(maxentry*4, 4, 1, 2);
        send_flash_request(maxentry*4, 4, 0, 2);
        //sencond_arr数组里面存的是lpn,将翻译页关联的映射项全部置为无效
        for(indexold = 0;indexold < MAP_SECOND_MAX_ENTRIES; indexold++){
            if(((second_arr[indexold]-page_num_for_2nd_map_table)/MAP_ENTRIES_PER_PAGE) == maxentry){
                opagemap[second_arr[indexold]].update = 0;
                opagemap[second_arr[indexold]].map_status = MAP_INVALID;
                opagemap[second_arr[indexold]].map_age = 0;
                second_arr[indexold]=-1;
                MAP_SECOND_NUM_ENTRIES--;
            }
        }
        //debug
        if(MAP_SECOND_NUM_ENTRIES+MC != MAP_SECOND_MAX_ENTRIES){
            printf("error in CCMT_Is_Full, maxentry=%d, MC=%d",maxentry, MC);
            assert(0);
        }
        MC = 0;
        entry_arr[maxentry] = 0;
        find_MC_entries(entry_arr,page_num_for_2nd_map_table);
    }
}

// 因为CPFTL命中CCMT加载到H-CMT中的逻辑存在混乱，故重新定义一个加载函数
void CPFTL_move_CCMT_to_HCMT(int req_lpn,int operation)
{
    int flag=-1,pos=-1,pos_2nd=-1,free_pos=-1;
    //    int temp;
    int limit_start=-1,limit_end=-1;

    //debug-value
    int last_second;
    int last_real;


    pos_2nd=search_table(second_arr,MAP_SECOND_MAX_ENTRIES,req_lpn);

    MC=0;
    find_MC_entries(entry_arr,page_num_for_2nd_map_table);
    limit_start=maxentry*MAP_ENTRIES_PER_PAGE+page_num_for_2nd_map_table;
    limit_end=(maxentry+1)*MAP_ENTRIES_PER_PAGE+page_num_for_2nd_map_table;
    if(req_lpn>=limit_start && req_lpn<=limit_end){
        flag=1;
    }
    entry = (second_arr[pos_2nd]-page_num_for_2nd_map_table)/MAP_ENTRIES_PER_PAGE;
    entry_arr[entry] -= 1;
    //MC=0;
    //find_MC_entries(entry_arr,page_num_for_2nd_map_table);

    //处理掉特殊情况
    if(flag!=-1 && MAP_REAL_NUM_ENTRIES == MAP_REAL_MAX_ENTRIES){
        //printf("real is full and CCMT hit need to load entry to  HCMT\n");
        find_real_min();
        if(opagemap[real_min].update==1){
            // 直接交换两者的位置
            pos = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
            opagemap[real_min].map_status = MAP_SECOND;
            second_arr[pos_2nd]=real_min;
            entry = (real_min-page_num_for_2nd_map_table)/MAP_ENTRIES_PER_PAGE;
            entry_arr[entry] += 1;
            real_arr[pos]=req_lpn;
            opagemap[req_lpn].map_status=MAP_REAL;
            opagemap[req_lpn].map_age=operation_time;
            operation_time++;
        }else{
            // 没有更新直接剔除到flash
            pos = search_table(real_arr,MAP_REAL_MAX_ENTRIES,real_min);
            real_arr[pos]=-1;
            opagemap[real_min].map_status = MAP_INVALID;
            opagemap[real_min].map_age = 0;
            MAP_REAL_NUM_ENTRIES--;
            //将real_arr[pos]存放刚刚命中的lpn
            real_arr[pos]=req_lpn;
            opagemap[req_lpn].map_status=MAP_REAL;
            MAP_REAL_NUM_ENTRIES++;
            opagemap[req_lpn].map_age=operation_time;
            operation_time++;
            second_arr[pos_2nd]=-1;
            MAP_SECOND_NUM_ENTRIES--;

        }
        //特殊情况处理完毕
    }else{
        //其他情况依旧采用之前的置换策略；
        last_real=MAP_REAL_NUM_ENTRIES;
        last_second=MAP_SECOND_NUM_ENTRIES;
        CPFTL_HCMT_Is_Full();
        pos=search_table(second_arr,MAP_SECOND_MAX_ENTRIES,req_lpn);
        if(pos==-1){
            printf("can not find blkno :%d in second_arr\n",req_lpn);
            assert(0);
        }
        second_arr[pos]=-1;
        MAP_SECOND_NUM_ENTRIES--;
        free_pos=find_free_pos(real_arr,MAP_REAL_MAX_ENTRIES);
        if(free_pos==-1){
            printf("can not find free pos in real_arr\n");
            assert(0);
        }
        real_arr[free_pos]=req_lpn;
        MAP_REAL_NUM_ENTRIES++;
        opagemap[req_lpn].map_status=MAP_REAL;
        opagemap[req_lpn].map_age=operation_time;
        operation_time++;

    }
    //data page operation
    if(operation==0){
        write_count++;
        opagemap[req_lpn].update = 1;
    }
    else
        read_count++;

    send_flash_request(req_lpn*4, 4, operation, 1);

}


void CPFTL_move_SCMT_to_HCMT(int blkno ,int operation)
{
    int free_pos=-1;
    CPFTL_HCMT_Is_Full();
    free_pos=find_free_pos(real_arr,MAP_REAL_MAX_ENTRIES);
    if(free_pos==-1){
        printf("can not find free pos in real_arr\n");
        assert(0);
    }
    real_arr[free_pos]=blkno;
    opagemap[blkno].map_status=MAP_REAL;
    opagemap[blkno].map_age=operation_time;
    operation_time++;
    MAP_REAL_NUM_ENTRIES++;
    //operation data pages
    if(operation==0){
        write_count++;
        opagemap[blkno].update = 1;
    }
    else
        read_count++;

    send_flash_request(blkno*4, 4, operation, 1);

}


// void chang_max_entry(int entry, int entry_count)
// {
//     if(entry_count > MC){
//         MC = entry_count;
//         maxentry = entry;
//     }
// }


void find_MC_entries(int *entryarr, int size)
{
	int i, temp_max = 0;
    for(i=0; i<size; i++){
        if(entryarr[i] > MC){
            MC = entryarr[i];
            maxentry = i;
        }
    }
}